package com.choudou5.solr.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.choudou5.solr.bean.SolrBaseBean;
import com.choudou5.solr.bean.SolrQueryResp;
import com.choudou5.solr.bean.common.PageResult;
import com.choudou5.solr.core.util.SolrHelper;
import org.apache.commons.lang.ArrayUtils;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.LazyDocument.LazyField;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.index.IndexableField;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.beans.DocumentObjectBinder;
import org.apache.solr.client.solrj.impl.XMLResponseParser;
import org.apache.solr.client.solrj.request.UpdateRequest;
import org.apache.solr.client.solrj.response.FacetField;
import org.apache.solr.client.solrj.response.QueryResponseExt;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.UpdateParams;
import org.apache.solr.common.util.ContentStream;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrCore;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.request.SolrRequestInfo;
import org.apache.solr.response.ResultContext;
import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.response.transform.DocTransformer;
import org.apache.solr.response.transform.TransformContext;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.search.DocIterator;
import org.apache.solr.search.DocList;
import org.apache.solr.search.ReturnFields;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.servlet.SolrRequestParsers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.Serializable;
import java.net.URLDecoder;
import java.util.*;

public class SolrJUtil {

	private static final Logger logger = LoggerFactory.getLogger(SolrJUtil.class);

	public static final String QUERY_ALL ="*:*";
	
	/** Query Type */
	public static final String QT_SELECT ="/select";
	public static final String QT_UPDATE ="/update";
	  
	/** Writer Type */
	public static final String WT_XML ="xml";

	
	private static SolrQuery getSolrQuery(String keyword, boolean commit){
		SolrQuery params = new SolrQuery();
		params.add("wt", WT_XML);
		if(keyword != null)
			params.add("q", keyword);
		if(commit){
			params.add(UpdateParams.COMMIT, "true" );
			params.add(UpdateParams.WAIT_SEARCHER, "false");
			//OPEN_SEARCHER
		}
		return params;
	}
	 
	public static SolrQuery getSolrQuery(){
		return getSolrQuery(null, false);
	}
	
	public static SolrQuery getSolrQuery(String keyword){
		return getSolrQuery(keyword, false);
	}
	
	public static SolrQuery getSolrQueryAll(){
		return getSolrQuery(QUERY_ALL, false);
	}
	
	public static String getSolrQueryStr(String key, String[] vals){
		if(ArrayUtils.isEmpty(vals))
			return null;
		StringBuilder kw = new StringBuilder(vals.length*(key.length()+5)+2);
		kw.append("(");
		for (int i = 0; i < vals.length; i++) {
			kw.append((i!=0?" OR ":"")+key+":"+vals[i]);
		}
		kw.append(")");
		return kw.toString();
	}
	
	
	public static SolrQuery getSolrQuery(boolean commit){
		return getSolrQuery(null, true);
	}
	
	/**
	 * 根据 主键删除
	 * @param schemaName
	 * @param pk
	 * @param pkVal
	 */
	public static <T extends Serializable> void deleteByPk(String schemaName, String pk, String pkVal){
		SolrCore core = SolrHelper.getCore(schemaName);
		delete(pk + ":" + pkVal, core);
	}
	
	/**
	 * 删除 模型数据
	 * @param delQuery 删除语句
	 * @param core
	 */
	public static <T extends Serializable> void delete(String delQuery, SolrCore core){
		SolrQueryResponse solrResp = new SolrQueryResponse();
		SolrQuery params = getSolrQuery(true);
		params.add("stream.body", "<delete><query>" + delQuery + "</query></delete>");
	    try {
	    	ArrayList<ContentStream> streams = new ArrayList<>(1);
	    	execute(QT_UPDATE, core, params, streams, solrResp);
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "delete fail", e);
		}
	}
	
	/**
	 * 添加 模型数据
	 * @param inputMap
	 * @param core
	 */
	public static <T extends Serializable> void add(Map<String, ?> inputMap, SolrCore core){
		SolrQueryResponse solrResp = new SolrQueryResponse();
		SolrQuery params = getSolrQuery(true);
	    try {
		    ArrayList<SolrInputDocument> docs =  new ArrayList<SolrInputDocument>(1);
		    SolrInputDocument doc = new SolrInputDocument();
		    for (String field : inputMap.keySet()) {
		    	doc.addField(field, inputMap.get(field));
			}
		    docs.add(doc);
		    Collection<ContentStream> streams = getContentStreams(docs);
	    	execute(QT_UPDATE, core, params, streams, solrResp);
	    	Exception ex = solrResp.getException();
	    	if(ex != null)
	    		throw new SolrException(ErrorCode.BAD_REQUEST, ex);
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "添加数据失败！"+e.getMessage(), e);
		}
	}
	
	/**
	 * 添加 模型数据
	 * @param bean
	 * @param core
	 */
	public static <T extends Serializable> void add(T bean, SolrCore core){
		SolrQueryResponse solrResp = new SolrQueryResponse();
		SolrQuery params = getSolrQuery(true);
	    try {
	    	Collection<ContentStream> streams = getContentStreams(bean);
	    	execute(QT_UPDATE, core, params, streams, solrResp);
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "add fail", e);
		}
	}
	
	/**
	 * 添加 模型数据集合
	 * @param beans
	 * @param core
	 */
	public static <T extends Serializable> void adds(List<T> beans, SolrCore core){
		SolrQueryResponse solrResp = new SolrQueryResponse();
		SolrQuery params = getSolrQuery(true);
	    try {
	    	Collection<ContentStream> streams = getContentStreams(beans);
	    	execute(QT_UPDATE, core, params, streams, solrResp);
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "adds fail", e);
		}
	}

	/**
	 * 添加 模型数据集合
	 * @param beans
	 * @param core
	 */
	public static <T extends Serializable> void addListMap(List<Map> beans, SolrCore core){
		SolrQueryResponse solrResp = new SolrQueryResponse();
		SolrQuery params = getSolrQuery(true);
		try {
			Collection<ContentStream> streams = getContentStreamsForListMap(beans);
			execute(QT_UPDATE, core, params, streams, solrResp);
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "adds fail", e);
		}
	}
	
	/**
	 * 添加 模型数据集合
	 * @param beans
	 * @param core
	 */
	public static <T extends Serializable> void adds(T[] beans, SolrCore core){
		SolrQueryResponse solrResp = new SolrQueryResponse();
		SolrQuery params = getSolrQuery(true);
	    try {
	    	Collection<ContentStream> streams = getContentStreams(beans);
	    	execute(QT_UPDATE, core, params, streams, solrResp);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获得 模型数据
	 * @param params
	 * @param core
	 * @param clasz
	 * @return
	 */
	public static <T extends Serializable> T get(SolrQuery params, SolrCore core, Class<T> clasz){
		SolrQueryResponse solrResp = new SolrQueryResponse();
	    try {
	    	ArrayList<ContentStream> streams = new ArrayList<>(1);
	    	SolrQueryRequest sreq = execute(QT_SELECT, core, params, streams, solrResp);
			SolrDocument doc = getDoc(solrResp, sreq);
			if(doc != null){
				DocumentObjectBinder binder = new DocumentObjectBinder();
				return binder.getBean(clasz, doc);
			}
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "get fail", e);
		}
	    return null;
	}
	
	/**
	 * 根据 主键 获取 doc
	 * @param schemaName
	 * @param pk
	 * @param pkVal
	 * @return
	 */
	public static SolrDocument getDocByPk(String schemaName, String pk, String pkVal){
		SolrCore core = SolrHelper.getCore(schemaName);
		return SolrJUtil.getDoc(SolrJUtil.getSolrQuery(pk+":"+pkVal), core);
	}
	
	/**
	 * 获得 模型数据
	 * @param params
	 * @param core
	 * @return
	 */
	public static SolrDocument getDoc(SolrQuery params, SolrCore core){
		SolrQueryResponse solrResp = new SolrQueryResponse();
	    try {
	    	ArrayList<ContentStream> streams = new ArrayList<>(1);
	    	SolrQueryRequest sreq = execute(QT_SELECT, core, params, streams, solrResp);
			return getDoc(solrResp, sreq);
		} catch (Exception e) {
			logger.error("getDoc fail", e);
			return null;
		}
	}
	
	
	/**
	 * 查询模型
	 * @param params
	 * @param core
	 * @param clasz
	 * @return
	 */
	public static <T extends Serializable> List<T> findList(SolrQuery params, SolrCore core, Class<T> clasz){
		SolrQueryResponse solrResp = new SolrQueryResponse();
	    try {
	    	params.setRows(1000);
	    	ArrayList<ContentStream> streams = new ArrayList<>(1);
	    	SolrQueryRequest sreq = execute(QT_SELECT, core, params, streams, solrResp);
			
			SolrDocumentList docList = getDocs(solrResp, sreq);
			if(docList != null){
				DocumentObjectBinder binder = new DocumentObjectBinder();
				return (List<T>) binder.getBeans(clasz, docList);
			}
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "findList fail", e);
		}
	    return null;
	}
	
	/**
	 * 查询 单字段值
	 * @param params
	 * @param core
	 * @param fieldName 返回字段名
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Serializable> List<T> findFieldValList(SolrQuery params, SolrCore core, String fieldName){
		SolrQueryResponse solrResp = new SolrQueryResponse();
	    try {
	    	ArrayList<ContentStream> streams = new ArrayList<>(1);
	    	params.setFields(fieldName);
	    	SolrQueryRequest sreq = execute(QT_SELECT, core, params, streams, solrResp);
	    	SolrDocumentList docs = getDocs(solrResp, sreq);
	    	List<T> result = new ArrayList<T>();
	    	if(docs != null){
	    		for (SolrDocument doc : docs) {
					result.add((T)doc.getFieldValue(fieldName));
				}
	    	}
	    	return result;
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "findList fail", e);
		}
	}
	
	
	/**
	 * 查询模型
	 * @param params
	 * @param core
	 * @return SolrDocumentList
	 */
	public static SolrDocumentList findList(SolrQuery params, SolrCore core){
		SolrQueryResponse solrResp = new SolrQueryResponse();
	    try {
	    	ArrayList<ContentStream> streams = new ArrayList<>(1);
	    	SolrQueryRequest sreq = execute(QT_SELECT, core, params, streams, solrResp);
			return getDocs(solrResp, sreq);
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "findList fail", e);
		}
	}
	
	public static <T extends SolrBaseBean> PageResult<T> findPage(SolrQuery params, SolrCore core, Class<T> clasz){
		SolrQueryResponse solrResp = new SolrQueryResponse();
		PageResult<T> page = new PageResult<T>();
	    try {
	    	ArrayList<ContentStream> streams = new ArrayList<>(1);
	    	SolrQueryRequest sreq = execute(QT_SELECT, core, params, streams, solrResp);
//			writeDocuments(name, (ResultContext)val, returnFields);
			SolrQueryResp resp = readDocListResp(params, sreq, solrResp);
//	    	SolrDocumentList solrDocList = getDocs(solrResp, sreq);
	    	page.setPageNo((params.getStart() / params.getRows()) + 1);
			page.setQt(resp.getQt());
	    	page.setPageSize(params.getRows());
	    	page.setTotalCount(resp.getNumFound());
			page.setQt(resp.getNumFound());
	    	if(resp != null){
				DocumentObjectBinder binder = new DocumentObjectBinder();
				page.setResult(binder.getBeans(clasz, resp));
			}
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "findPage fail", e);
		}
	    return page;
	}
	
	public static PageResult<SolrDocument> findDocPage(SolrQuery params, SolrCore core){
		SolrQueryResponse solrResp = new SolrQueryResponse();
		PageResult<SolrDocument> page = new PageResult<SolrDocument>();
	    try {
			XMLResponseParser parser = new XMLResponseParser();
			params.set(CommonParams.WT, parser.getWriterType());
			params.set(CommonParams.VERSION, parser.getVersion());
	    	ArrayList<ContentStream> streams = new ArrayList<>(1);
	    	SolrQueryRequest sreq = execute(QT_SELECT, core, params, streams, solrResp);
			SolrQueryResp resp = readDocListResp(params, sreq, solrResp);
//	    	SolrDocumentList solrDocList = getDocs(solrResp, sreq);
			page.setQt(resp.getQt());
	    	page.setPageNo((params.getStart() / params.getRows()) + 1);
	    	page.setPageSize(params.getRows());
	    	page.setTotalCount(resp.getNumFound());
	    	page.setResult(resp);
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "findPage fail", e);
		}
	    return page;
	}

	private static void wrapHighlight(SolrDocumentList solrDocList){
		if(CollUtil.isNotEmpty(solrDocList)){

		}
	}

	public static long findTotal(SolrQuery params, SolrCore core){
		SolrQueryResponse solrResp = new SolrQueryResponse();
		params.setStart(0).setRows(1);
		try {
			ArrayList<ContentStream> streams = new ArrayList<>(1);
			SolrQueryRequest sreq = execute(QT_SELECT, core, params, streams, solrResp);
			SolrQueryResp solrDocList = readDocListResp(params, sreq, solrResp);
			return solrDocList.getNumFound();
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "findTotal fail", e);
		}
	}

	public static List<String> getFacetDataList(String field, SolrQuery params, SolrCore core){
		SolrQueryResponse solrResp = new SolrQueryResponse();
		params.setStart(0).setRows(1);
		try {
			ArrayList<ContentStream> streams = new ArrayList<>(1);
			SolrQueryRequest sreq = execute(QT_SELECT, core, params, streams, solrResp);
			SolrQueryResp resp = readDocListResp(params, sreq, solrResp);
			return resp.getFacetDataListMap().get(field);
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "getFacetDataList fail", e);
		}
	}

	public static List<String> getFacetDataList(String field, SolrQuery params, SolrCore core, boolean onlyGetFacetKey){
		SolrQueryResponse solrResp = new SolrQueryResponse();
		params.setStart(0).setRows(1);
		try {
			ArrayList<ContentStream> streams = new ArrayList<>(1);
			SolrQueryRequest sreq = execute(QT_SELECT, core, params, streams, solrResp);
			SolrQueryResp resp = readDocListResp(params, sreq, solrResp, onlyGetFacetKey, null);
			return resp.getFacetDataListMap().get(field);
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "getFacetDataList fail", e);
		}
	}

	public static List<String> getFacetDateList(String field, SolrQuery params, SolrCore core, String dateFormat){
		SolrQueryResponse solrResp = new SolrQueryResponse();
		params.setStart(0).setRows(1);
		try {
			ArrayList<ContentStream> streams = new ArrayList<>(1);
			SolrQueryRequest sreq = execute(QT_SELECT, core, params, streams, solrResp);
			SolrQueryResp resp = readDocListResp(params, sreq, solrResp, dateFormat);
			return resp.getFacetDataListMap().get(field);
		} catch (Exception e) {
			throw new SolrException(ErrorCode.BAD_REQUEST, "getFacetDateList fail", e);
		}
	}


	private static SolrQueryRequest execute(String qt, SolrCore core, SolrQuery params, Collection<ContentStream> streams, SolrQueryResponse resp) throws Exception{
		logger.info("query str:" + URLDecoder.decode(params.toString(), "UTF-8"));
		final SolrConfig config = core.getSolrConfig();
		SolrRequestParsers parser = config.getRequestParsers();
		SolrQueryRequest sreq = parser.buildRequestFrom(core, params, streams);
		sreq.getContext().put( "path", qt);
		SolrRequestInfo.setRequestInfo(new SolrRequestInfo(sreq, resp));
		
		SolrRequestHandler handler = core.getRequestHandler(qt);
		sreq.getCore().execute( handler, sreq, resp);
		SolrRequestInfo.clearRequestInfo();
		return sreq;
	}


	public static SolrQueryResp readDocListResp(SolrQuery params, SolrQueryRequest req, SolrQueryResponse solrResp) throws IOException {
		return readDocListResp(params, req, solrResp, false, null);
	}

	public static SolrQueryResp readDocListResp(SolrQuery params, SolrQueryRequest req, SolrQueryResponse solrResp, String dateFormat) throws IOException {
		return readDocListResp(params, req, solrResp, false, dateFormat);
	}

	public static SolrQueryResp readDocListResp(SolrQuery params, SolrQueryRequest req, SolrQueryResponse solrResp, boolean onlyGetFacetKey, String dateFormat) throws IOException {
		NamedList values = solrResp.getValues();
		QueryResponseExt response = new QueryResponseExt();
		response.setResponse(values, req, solrResp.getReturnFields());
		SolrQueryResp resp = new SolrQueryResp(response.getResults(), response.getQTime());
		if(!resp.isEmpty()){
			//高亮
			if(params.getHighlight())
				warpHighLighting(params, response, resp);
			//分片 日期
			warpFacetDateList(response, resp, dateFormat, onlyGetFacetKey);
		}
		return resp;
	}


	private static void warpFacetDateList(QueryResponseExt response, SolrQueryResp resp, String dateFormat, boolean onlyGetKey){
		List<FacetField> facetDatas = null;
		if(dateFormat != null){
			facetDatas = response.getFacetDates();;
		}else{
			facetDatas = response.getFacetFields();
		}

		if(CollUtil.isNotEmpty(facetDatas)){
			List<String> list = null;
			//遍历分片数据
			for (FacetField facetData : facetDatas) {
				list = new ArrayList<>();
				List<FacetField.Count> counts = facetData.getValues();
				for (FacetField.Count count : counts) {
					if(dateFormat != null){
						if(onlyGetKey)
							list.add(DateUtil.format(DateUtil.formatToSolrDate(count.getName()), dateFormat));
						else
							list.add(DateUtil.format(DateUtil.formatToSolrDate(count.getName()), dateFormat)+"="+count.getCount());
					}
					else{
						if(onlyGetKey)
							list.add(count.getName());
						else
							list.add(count.getName() + "=" + count.getCount());
					}
				}
				resp.addFacetDataData(facetData.getName(), list);
			}
		}
	}


	private static void warpHighLighting(SolrQuery params, QueryResponseExt response, SolrQueryResp resp){
		String[] hlFields = params.getHighlightFields();//高亮字段
		Map<String, Map<String, Object>> hl = response.getHighlighting();
		if(MapUtil.isNotEmpty(hl)){
			//遍历高亮数据
			for (Map.Entry<String, Map<String, Object>> entry : hl.entrySet()) {
				//ID对应的 高亮记录
				Map<String, Object> hlResult = entry.getValue();
				if(MapUtil.isNotEmpty(hlResult)){
					//为设置高亮的字段 重新设值
					for (String hlField : hlFields) {
						Object hlValues = hlResult.get(hlField);//高亮字段 值（为list 是因为 有可能该字段存放的是 list）
						if(hlValues != null) {
							//包装 高亮数据
							warpHighLighting(resp, entry.getKey(), hlField, hlValues, params.getHighlightSimplePre(), params.getHighlightSimplePost());
						}
					}
				}
			}
		}
	}


	/**
	 * 包装 高亮数据
	 * @param docList
	 * @param pkId 主键ID
	 * @param hlField 高亮字段
	 * @param hlValues  高亮后的值
	 * @return SolrDocumentList
	 */
	private static void warpHighLighting(SolrDocumentList docList, String pkId, String hlField, Object hlValues, String hl_simple_pre, String hl_simple_post){
		for (SolrDocument doc : docList) {
			String id  = (String)doc.get("id");
			if(pkId.equals(id)){
				//拿到 高亮字段 原值
				Object hlFieldOriginalVal = doc.get(hlField);
				//原值为集合时
				if(hlFieldOriginalVal instanceof Collection) {
					List<String> array = (List<String>)hlFieldOriginalVal;
					List<String> hlValueList =  (List)hlValues;
					for (String child : hlValueList) {
						//替换 高亮值的 前后描述
						String hlVal = child.replace(hl_simple_pre, "").replace(hl_simple_post, "");
						int dex = array.indexOf(hlVal);
						if(dex != -1){
							array.set(dex, child);
						}
					}
				}else{
					if(hlValues instanceof String[]){
						doc.setField(hlField, ((String[])hlValues)[0]);
					}else{
						doc.setField(hlField, hlValues);
					}
				}
			}
		}
	}


	/**
	 * 获得 文档列表
	 * @param resp
	 * @param req
	 * @link http://www.programcreek.com/java-api-examples/index.php?api=org.apache.solr.response.ResultContext
	 * @return
	 * @throws IOException
	 */
	public static SolrDocumentList getDocs(SolrQueryResponse resp, SolrQueryRequest req) throws IOException {
		NamedList<?> list = resp.getValues();
		ResultContext res = (ResultContext) list.get("response");
		ReturnFields fields = resp.getReturnFields();
		
		DocList ids = res.docs;
		TransformContext context = new TransformContext();
		context.query = res.query;
		context.wantsScores = fields.wantsScore() && ids.hasScores();
		context.req = req;

		SolrDocumentList docs = new SolrDocumentList();

		docs.setNumFound(ids == null ? 0 : ids.matches());
		docs.setStart(ids == null ? 0 : ids.offset());
		docs.setMaxScore(ids == null ? 0 : ids.maxScore());

		DocTransformer transformer = fields.getTransformer();
		context.searcher = req.getSearcher();
		context.iterator = ids.iterator();
		if( transformer != null ) {
			transformer.setContext(context);
		}
		
		Set<String> fieldNames = fields.getLuceneFieldNames();
		int size = ids.size();
		IndexSchema schema = req.getSchema();
		DocIterator iterator = ids.iterator();
		for (int i=0; i<size; i++) {
			if(!iterator.hasNext())
				break;
			docs.add(getSolrDocument(iterator.nextDoc(), req.getSearcher(), transformer, schema, fieldNames));
		}
		if( transformer != null ) {
			transformer.setContext( null );
		}
		return docs;
	}
	
	/**
	 * 获得 文档
	 * @param resp
	 * @param req
	 * @return
	 * @throws IOException
	 */
	public static SolrDocument getDoc(SolrQueryResponse resp, SolrQueryRequest req) throws IOException {
		NamedList<?> list = resp.getValues();
		ResultContext res = (ResultContext) list.get("response");
		if(res == null) return null;
		ReturnFields fields = resp.getReturnFields();
		
		DocList ids = res.docs;
		TransformContext context = new TransformContext();
		context.query = res.query;
		context.wantsScores = fields.wantsScore() && ids.hasScores();
		context.req = req;

		DocTransformer transformer = fields.getTransformer();
		context.searcher = req.getSearcher();
		context.iterator = ids.iterator();
		if( transformer != null ) {
			transformer.setContext(context);
		}
		Set<String> fieldNames = fields.getLuceneFieldNames();
		IndexSchema schema = req.getSchema();
		
		if(!context.iterator.hasNext())
			return null;
		SolrDocument doc = getSolrDocument(context.iterator.nextDoc(), req.getSearcher(), transformer, schema, fieldNames);
		if( transformer != null ) {
			transformer.setContext( null );
		}
		return doc;
	}
	
	private static SolrDocument getSolrDocument(int id, SolrIndexSearcher searcher, DocTransformer transformer, IndexSchema schema, Set<String> fieldNames) throws IOException{
		Document doc = searcher.doc(id, fieldNames);
//		Set<String> fieldNames = fieldNameSet;//schema.getFields().keySet();
		SolrDocument d = new SolrDocument();
		for (String fname : fieldNames) {
			IndexableField val = doc.getField(fname);
			
			if ((val instanceof StoredField) || (val instanceof Field) || val instanceof LazyField) {
				Object o = schema.getField(fname).getType().toObject(val);
				IndexableField[] arr = doc.getFields(fname);
				if(arr != null && arr.length > 1){
					Object[] valObjs = new Object[arr.length];
					for (int i = 0; i < valObjs.length; i++) {
						valObjs[i] = schema.getField(fname).getType().toObject(arr[i]);
					}
					d.setField(fname, valObjs);
				}else{
					d.setField(fname, o);
				}
			}
			if( transformer != null) {
				transformer.transform(d, id);
			}
		}
		return d;
	}


	private static SolrDocument toSolrDocument(Document doc, IndexSchema schema, Set<String> fieldNames) throws IOException{
		SolrDocument d = new SolrDocument();
		for (String fname : fieldNames) {
			IndexableField val = doc.getField(fname);
			if ((val instanceof StoredField) || (val instanceof Field) || val instanceof LazyField) {
				Object o = schema.getField(fname).getType().toObject(val);
				IndexableField[] arr = doc.getFields(fname);
				if(arr != null && arr.length > 1){
					Object[] valObjs = new Object[arr.length];
					for (int i = 0; i < valObjs.length; i++) {
						valObjs[i] = schema.getField(fname).getType().toObject(arr[i]);
					}
					d.setField(fname, valObjs);
				}else{
					d.setField(fname, o);
				}
			}
		}
		return d;
	}

	
	
	private static <T extends Serializable> Collection<ContentStream> getContentStreams(T bean) throws SolrServerException, IOException {
		DocumentObjectBinder binder = new DocumentObjectBinder();
	    ArrayList<SolrInputDocument> docs =  new ArrayList<SolrInputDocument>(1);
	    docs.add(binder.toSolrInputDocument(bean));
	    return getContentStreams(docs);
	}
	
	private static <T extends Serializable> Collection<ContentStream> getContentStreams(T[] beans) throws SolrServerException, IOException {
		DocumentObjectBinder binder = new DocumentObjectBinder();
	    ArrayList<SolrInputDocument> docs =  new ArrayList<SolrInputDocument>(1);
	    for (T bean : beans) {
	        docs.add(binder.toSolrInputDocument(bean));
	    }
	    return getContentStreams(docs);
	}
	
	private static <T extends Serializable> Collection<ContentStream> getContentStreams(List<T> beans) throws SolrServerException, IOException {
		DocumentObjectBinder binder = new DocumentObjectBinder();
	    ArrayList<SolrInputDocument> docs =  new ArrayList<SolrInputDocument>(1);
	    for (T bean : beans) {
	        docs.add(binder.toSolrInputDocument(bean));
	    }
	    return getContentStreams(docs);
	}
	
	
	private static <T extends Serializable> Collection<ContentStream> getContentStreams(ArrayList<SolrInputDocument> docs) throws SolrServerException, IOException {
	    UpdateRequest req = new UpdateRequest();
	    req.add(docs);
	    req.setCommitWithin(-1);
	    return req.getContentStreams();
	}

	
	@SuppressWarnings("unused")
	private static Collection<ContentStream> getContentStreams(Collection<?> beans) throws SolrServerException, IOException {
		DocumentObjectBinder binder = new DocumentObjectBinder();
	    ArrayList<SolrInputDocument> docs =  new ArrayList<>(beans.size());
	    for (Object bean : beans) {
	      docs.add(binder.toSolrInputDocument(bean));
	    }
	    UpdateRequest req = new UpdateRequest();
	    req.add(docs);
	    req.setCommitWithin(-1);
	    return req.getContentStreams();
	}

	@SuppressWarnings("unused")
	private static Collection<ContentStream> getContentStreamsForListMap(Collection<Map> beans) throws SolrServerException, IOException {
		DocumentObjectBinder binder = new DocumentObjectBinder();
		ArrayList<SolrInputDocument> docs =  new ArrayList<>(beans.size());
		for (Map<String, Object> inputMap : beans) {
			SolrInputDocument doc = new SolrInputDocument();
			for (String field : inputMap.keySet()) {
				doc.addField(field, inputMap.get(field));
			}
			docs.add(doc);
		}
		UpdateRequest req = new UpdateRequest();
		req.add(docs);
		req.setCommitWithin(-1);
		return req.getContentStreams();
	}
	
}
